#   This program is free software; you can redistribute it and/or modify
#   it under the terms of the version 3 of the GNU Lesser General Public License
#   as published by the Free Software Foundation.
#
#   This program is distributed in the hope that it will be useful,
#   but WITHOUT ANY WARRANTY; without even the implied warranty of
#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#   GNU General Public License for more details.
#
#   You should have received a copy of the GNU Lesser General Public License
#   along with this program; if not, write to the Free Software
#   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#
# Copyright (c) NEC Deutschland GmbH, NEC HPC Europe
#
# $Id$

import os
import logging
from marshaller import Marshaller
from shutil import rmtree


class FSMarshaller(Marshaller):

    ## Creates directory path.
    # base_dir_path: directory path to be created
    def __init__( self, base_dir_path ):
        Marshaller.__init__( self )
        self._base_dir_path = None
        self._clean = False
        if not os.path.exists( base_dir_path ):
            try:
                os.makedirs( base_dir_path )
                self._base_dir_path = base_dir_path
            except:
                logging.error( "can't create directory %s" % base_dir_path )
        else:
            self._base_dir_path = base_dir_path

    def is_selective( self ):
        "Marshal only dirty objects."
        return True

    ## Helper method that writes value in a file named key. If there is no value it removes the file.
    def _writeRepr( self, key, value ):
        if not self._base_dir_path:
            return
        if self._clean:
            return
        file_path = os.path.join( self._base_dir_path, key )
        if len( value ) > 0:
            try:
                open( file_path, "w+" ).write( value )
            except:
                logging.error( "can't write to %s" % file_path )
        else:
            try:
                os.unlink( file_path )
            except:
                pass

    ## Marshall primitive types like string, long etc.
    # Use key as file name. Store value as Python __repr__ into it.
    def writeObj( self, key, value ):
        try:
            self._writeRepr( key, repr( value ) )
        except:
            logging.error( "can't write to file %s" % key )

    writeDouble =  writeInt64 = writeString = writeBoolean = writeSimpleSequence = writeObj

    ## Marshall value, that is of type Sequence into own sub-directory.
    # Use current directory, add key and use it as new base directory.
    def writeSequence( self, key, value ):
        if not self._base_dir_path:
            return
        if value is not None:
            value.marshal( FSMarshaller( os.path.join( self._base_dir_path, key ) ) )

    ## Marshall value that is of type Map or Struct into own sub-directory.
    # Add all primitive attributes as key-value pairs to current directory and
    # descend for complex elements into directory tree.
    def writeMap( self, key, value ):
        if not self._base_dir_path:
            return
        sub_path = os.path.join( self._base_dir_path, key )
        if value is not None:
            if len( key ) > 0:
                sub_element_marshaller = FSMarshaller( sub_path )
            else:
                # This is the special case that we marshal the object without passing a key!
                #
                # use a key starting with '__', must be unique
                keys = [key for key in os.listdir( self._base_dir_path ) if (key.startswith( '__' )) ]
                __i = 0
                while "__%d" % __i in keys:
                    __i = __i + 1
                key = "__%d" % __i
                sub_path = os.path.join( self._base_dir_path, key )
                sub_element_marshaller = FSMarshaller( sub_path )

            value.marshal( sub_element_marshaller )
            sub_element_marshaller._writeRepr( "__class__", value.__class__.__name__.split()[-1] )
        else:
            # None value means: delete object!!!
            try:
                rmtree( sub_path )
            except:
                logging.error( "can't remove directory %s" % sub_path )

    writeStruct = writeMap
